<?php
/**
 * Created by PhpStorm.
 * User: admin
 * Date: 2022/12/29
 * Time: 10:11
 */
namespace app\index\service;

use think\Exception;
use think\facade\Cache;
use think\facade\Db;
use think\facade\Log;

class DyOpenService{

    private $baseUrl;
    private $appId;
    private $appsecret;
    private $token;//Token(令牌)
    private $salt;//SALT


    public function __construct(){
        $config=config('system.dyDeveloper');
        $this->appId=$config['appid'];//应用id
        $this->appsecret=$config['appsecret'];
        $this->token = $config['token'];
        $this->salt = $config['salt'];
        //获取个推的配置
        $this->baseUrl = "https://developer.toutiao.com/api/apps/v2/";
    }


    /**
     * 获取token
     * @return mixed
     * @throws Exception
     */
    public function getAccessToken(){
        //首先从缓存中
        $token = Cache::get('dyAccessToken-'.$this->appId);
        if(empty($token)){
            $param['appid'] = $this->appId;
            $param['secret'] = $this->appsecret;
            $param['grant_type'] = 'client_credential';
            $result = http_post_json($this->baseUrl.'token',$param);
            $result = json_decode($result,true);
            if($result['err_no'] !== 0){
                throw new Exception($result['err_tips'],$result['err_no']);
            }
            $token = $result['data']['access_token'];
            Cache::set('dyAccessToken-'.$this->appId,$token,5000);
        }
        return $token;
    }



    /**
     * 获取token(新版)
     * @return mixed
     * @throws Exception
     */
    public function getNewAccessToken(){
        //首先从缓存中
        $token = Cache::get('dyAccessTokenNew-'.$this->appId);
        if(empty($token)){
            $param['client_key'] = $this->appId;
            $param['client_secret'] = $this->appsecret;
            $param['grant_type'] = 'client_credential';
            $result = http_post_json('https://open.douyin.com/oauth/client_token/',$param);
            $result = json_decode($result,true);
            if($result['data']['error_code'] !== 0){
                throw new Exception($result['data']['description'],$result['data']['error_code']);
            }
            $token = $result['data']['access_token'];
            Cache::set('dyAccessTokenNew-'.$this->appId,$token,5000);
        }
        return $token;
    }


    /**
     * 登录解析
     * @param $code
     * @param string $anonymous_code
     * @return mixed
     * @throws Exception
     */
    public function login($code,$anonymous_code=''){
        $param['appid'] = $this->appId;
        $param['secret'] = $this->appsecret;
        $param['code'] = $code;
        $param['anonymous_code'] = $anonymous_code;
        $result = http_post_json($this->baseUrl.'jscode2session',$param);
        $result = json_decode($result,true);
        if($result['err_no'] !== 0){
            throw new Exception($result['err_tips'],$result['err_no']);
        }
        return $result['data'];
    }


    /**
     * 获取抖音手机号
     * @param $code
     * @param $encryptedData
     * @param $iv
     * @return string
     * @throws Exception
     */
    public function getPhone($code,$encryptedData,$iv){
        $param['appid'] = $this->appId;
        $param['secret'] = $this->appsecret;
        $param['code'] = $code;
        $result = http_post_json($this->baseUrl.'jscode2session',$param);
        $result = json_decode($result,true);
        if ($result['err_no'] == 0){
            $key = $result['data']['session_key'];
            //解密数据
            $phone = openssl_decrypt(base64_decode($encryptedData,true), 'AES-128-CBC', base64_decode($key), OPENSSL_RAW_DATA, base64_decode($iv));
            if (!$phone){
                throw new Exception('手机号信息解密失败',0);
            }else{
                $phone = json_decode($phone,true)['phoneNumber'];
                $result1 = $result['data'];
                $result1['phoneNumber'] = $phone;
                return $result1;
            }
        }else{
            throw new Exception($result['err_tips'],$result['err_no']);
        }
    }


    /**
     * @param $path
     * @return mixed
     * @throws Exception
     */
    public function createQRCode($path){
        $param['access_token'] = $this->getAccessToken();
        $param['appname'] = 'douyin';
        $param['path'] = $path;
        $param['set_icon'] = true;
        $result = http_post_json('https://developer.toutiao.com/api/apps/qrcode',$param);
        return $result;
    }


    /**
     * 抖音生成预支付订单
     * @param $order_no
     * @param $total_amount
     * @param $subject
     * @param $body
     * @param $notify_url
     * @return mixed
     * @throws Exception
     */
    public function createOrder($order_no,$total_amount,$subject,$body,$notify_url){
        $param['app_id'] = $this->appId;
        $param['out_order_no'] = $order_no;
        $param['total_amount'] = round($total_amount*100);
        $param['subject'] = $subject;//商品描述。 长度限制不超过 128 字节且不超过 42 字符
        $param['body'] = $body;//商品详情 长度限制不超过 128 字节且不超过 42 字符
        $param['valid_time'] = 900;//过期时间，即15分钟
        $param['notify_url'] = $notify_url;//回调地址
        $param['disable_msg'] = 0;//是否屏蔽支付完成后推送用户抖音消息，1-屏蔽 0-非屏蔽，默认为0。 特别注意： 若接入POI, 请传1。因为POI订单体系会发消息，所以不用再接收一次担保支付推送消息，
        $sign = $this->getSign($param);
        $param['sign'] = $sign;//生成的签名
        $result = http_post_json('https://developer.toutiao.com/api/apps/ecpay/v1/create_order',$param);
        $result = json_decode($result,true);
        if($result['err_no'] !== 0){
            throw new Exception($result['err_tips'],$result['err_no']);
        }
        return $result['data'];
    }


    /**
     * 新版交易系统下单
     * @param $openid
     * @param $order_no
     * @param $order_id
     * @param $total_amount
     * @param $subject
     * @param $body
     * @param $notify_url
     * @return mixed
     * @throws Exception
     */
    public function createNewOrder($openid,$order_no,$order_id,$total_amount,$notify_url){
        $param['goods_list'] = [
            [
                'quantity'=>1,
                'goods_id'=>'goods_1',
                'goods_id_type'=>2,
                'total_amount'=>$total_amount*100,
                'open_id'=>$openid,
                'pay_notify_url'=>$notify_url,
                'out_order_no'=>$order_no,
                'order_entry_schema'=>[
                    'path'=>'pages/order/order_det?id='.$order_id
                ],
            ]
        ];
        $result = http_post_json('https://open.douyin.com/api/apps/trade/v2/order/create_order',$param,$this->getAccessToken());
        $result = json_decode($result,true);
        if(!isset($result['data']['order_id'])){
            throw new Exception($result['data']['description'],$result['data']['error_code']);
        }
        return $result['data'];
    }




    /**
     * 推送订单到抖音
     * @param $openid
     * @param $out_trade_no
     * @param $order_id
     * @param $create_time
     * @param $total_price
     * @param $title
     * @return mixed
     * @throws Exception
     */
    //https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/server/ecpay/order/order-sync
    public function pushOrder($openid,$out_trade_no,$order_id,$create_time,$total_price,$title){
        //$param['client_key'] = '';//第三方在抖音开放平台申请的 ClientKey
        $param['access_token'] = $this->getAccessToken();
        $param['app_name'] = 'douyin';
        $param['open_id'] = $openid;
        $order_detail['order_id'] = $out_trade_no;//开发者侧业务单号。用作幂等控制
        $order_detail['create_time'] = $create_time*1000;
        $order_detail['status'] = '已支付';
        $order_detail['amount'] = 1;
        $order_detail['total_price'] = ceil($total_price*100);//订单总价，单位为分
        //$order_detail['detail_url'] = 'pages/order/order_det?id='.$order_id;//小程序订单详情页 path，长度<=1024 byte (备注：该路径需要保证在小程序内配置过，相对路径即可）
        $order_detail['detail_url'] = 'pages/tabbar/home/index';
        $order_detail['item_list'] = [
            [
                'item_code'=>'1',//开发者侧商品 ID，长度 <= 64 byte
                'img'=>'https://lvpaiqiche.oss-cn-hangzhou.aliyuncs.com/image/20230825/20230825184251_4vg6.png',//子订单商品图片 URL，长度 <= 512 byte
                'title'=>$title,//子订单商品图片 URL，长度 <= 512 byte
                'amount'=>1,//单类商品的数目
                'price'=>ceil($total_price*100),//单类商品的总价，单位为分
            ]
        ];
        Log::info('orderDetail：'.json_encode($order_detail));
        $param['order_detail'] = json_encode($order_detail);//json string，根据不同订单类型有不同的结构体，请参见 order_detail 字段说明（json string）
        $param['order_status'] = 1;
        $param['order_type'] = 0;
        $param['update_time'] = time()*1000;

        $result = http_post_json('https://developer.toutiao.com/api/apps/order/v2/push',$param);
        Log::info('推送订单到抖音：'.$result);
        $res = json_decode($result,true);
        if (isset($res['err_code']) && $res['err_code'] == 0) {
            $status = 1;
        } elseif (isset($res['err_code']) && $res['err_code'] == '40006'){
            $status = 0;
        } else {
            $status = 2;
        }
        Db::name('pay_order')
            ->where('is_pay', 1)
            ->where('id', $order_id)
            ->update([
                'is_push_dy' => $status
            ]);
        return $res;
    }


    /**
     * 退款
     * @param $order_no
     * @param $out_refund_no
     * @param $reason
     * @param $refund_amount
     * @return mixed
     * @throws Exception
     */
    public function createRefund($order_no,$out_refund_no,$reason,$refund_amount){
        $param['app_id'] = $this->appId;
        $param['out_order_no'] = $order_no;//订单号
        $param['out_refund_no'] = $out_refund_no;//商户分配退款号，保证在商户中唯一
        $param['reason'] = $reason;//退款原因
        $param['refund_amount'] = $refund_amount*100;//退款金额
        $sign = $this->getSign($param);
        $param['sign'] = $sign;//生成的签名
        $result = http_post_json('https://developer.toutiao.com/api/apps/ecpay/v1/create_refund',$param);
        $result = json_decode($result,true);
        if($result['err_no'] !== 0){
            throw new Exception($result['err_tips'],$result['err_no']);
        }
        return $result['refund_no'];
    }


    /**
     * 生成支付签名（请求支付接口的签名）
     * @param $map
     * @return string
     */
    private function getSign($map) {
        $rList = array();
        foreach($map as $k =>$v) {
            if ($k == "other_settle_params" || $k == "app_id" || $k == "sign" || $k == "thirdparty_id")
                continue;
            $value = trim(strval($v));
            $len = strlen($value);
            if ($len > 1 && substr($value, 0,1)=="\"" && substr($value,$len, $len-1)=="\"")
                $value = substr($value,1, $len-1);
            $value = trim($value);
            if ($value == "" || $value == "null")
                continue;
            array_push($rList, $value);
        }
        array_push($rList, $this->salt);
        sort($rList, 2);
        return md5(implode('&', $rList));
    }


    /**
     * 获取回调的签名
     * @param array $body
     * @param string $token
     * @return string
     */
    public function getNotifySign(array $body){
        $filtered = [];
        foreach ($body as $key => $value) {
            if (in_array($key, ['msg_signature', 'type'])) {
                continue;
            }
            $filtered[] =
                is_string($value)
                    ? trim($value)
                    : $value;
        }
        $filtered[] = trim($this->token);
        sort($filtered, SORT_STRING);
        $filtered = trim(implode('', $filtered));
        return sha1($filtered);
    }

    /**
     * 推送订单到抖音平台
     * @param $openid
     * @param $out_trade_no
     * @param $create_time
     * @param $total_price
     * @param $title
     * @return mixed
     * @throws Exception
     */
    public function pushOrdertToDy($openid,$out_trade_no,$create_time,$total_price,$title){
        // 商品详情
        $order_detail = [
            'order_id' => $out_trade_no,
            'create_time' => $create_time*1000,
            'status' => '已支付',
            'amount' => 1, // 商品数量
            'total_price' => $total_price*100, // 订单总价，单位为分
            'detail_url' =>  'pages/tabbar/home/index',
            'item_list' => [[
                'item_code'=>'1',//开发者侧商品 ID，长度 <= 64 byte
                'img'=>'https://lvpaiqiche.oss-cn-hangzhou.aliyuncs.com/image/20230825/20230825184251_4vg6.png',//子订单商品图片 URL，长度 <= 512 byte
                'title'=>$title,//子订单商品图片 URL，长度 <= 512 byte
                'amount'=>1,//单类商品的数目
                'price'=>$total_price*100,//单类商品的总价，单位为分
            ]]
        ];
        Log::info('orderDetail：'.json_encode($order_detail));
        // 订单数据
        $param = [
            'access_token' => $this->getAccessToken(),
            'app_name' => 'douyin',
            'open_id' => $openid,
            'order_status' => 1,
            'order_type' => 0,
            'order_detail' => json_encode($order_detail),
            'update_time' => time()*1000
        ];

        // 同步订单
        $result = http_post_json('https://developer.toutiao.com/api/apps/order/v2/push',$param);
        Log::info('推送订单到抖音平台 result：'.$result);
        return json_decode($result,true);
    }
}